// Copyright (C) 2006 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Javascript file for adding Google Checkout buttons to Yahoo! Stores pages
//
// Works in one of two ways:
//  - If invoked without any parameters, adds a checkout button to the end of
//    the page (this mode is useful for adding checkout functioality onto a page
//    after the page is fully loaded).
//  - If variable gcMode = "inplace" when this file is run, puts the checkout
//    button at that point in the page. This mode is useful for including this
//    file from a <script> tag, as follows:
//      <script>gcMode = "inplace";</script>
//      <script src="add-checkout.js"></script>

// When the checkout button is clicked, the script walks through the DOM looking
// for the table with the current cart contents in it and remarshalls them into
// a set of "items", which it then converts into form parameters and submits to
// another script: this secondary script (in this implementation,
// processCart.php) is expected to convert the form parameters to a Google
// Checkout cart, submit it to Google Checkout via the alternate cart posting
// method, and return a 302 redirect to the user.

// There are a couple of things that need to be configured in this script:
//  - the URL for the script that does cart conversion
//  - the merchant ID (used for constructing the URL to fetch the button image)
// You can find these locations by searching for EDIT_ME

// This function walks all of the child nodes of a node, stitching
// the text nodes together and returning the complete string.
function innerText(node) {
  var text = "";
  for (var i = 0; i < node.childNodes.length; ++i) {
    var child = node.childNodes[i];
    if (child.nodeType == 3) { // TEXT_NODE
      text = text + child.nodeValue;
    } else {
      text = text + innerText(child);
    }
  }
  return text;
}

// The cart table has a row that is one of the first 3 rows with
// column headers. Return column locations and the header row index.

// TODO: Some stores have an "Item" header that spans two columns in
// item rows: the first has the image, the second has the item
// description
function getCartTableInfo(table) {
  for (var i = 0; i < table.rows.length && i < 3; ++i) {
    var row = table.rows[i];
    var tableInfo = new Object();
    for (var j = 0; j < row.cells.length; ++j) {
      var text = innerText(row.cells[j]).toLowerCase();
      if (text.indexOf("item") >= 0) {
        tableInfo['item'] = j;
      } else if (text.indexOf("qty") >= 0 || (text.indexOf("quantity") >= 0)) {
        tableInfo['quantity'] = j;
      } else if (text.indexOf("unit") >= 0 && text.indexOf("price") >= 0) {
        tableInfo['price'] = j;
      } else if (text.indexOf("options") >= 0) {
        tableInfo['options'] = j;
      }
    }
    if (typeof(tableInfo['item']) != "undefined" &&
        typeof(tableInfo['quantity']) != "undefined" &&
        typeof(tableInfo['price']) != "undefined") {
      tableInfo['headerRow'] = i;
      return tableInfo;
    }
  }
  return null;
}

function findItemsTable() {
  var tables = document.body.getElementsByTagName("TABLE");
  for (var i = 0; i < tables.length; ++i) {
    var info = getCartTableInfo(tables[i]);
    if (info) {
      info['table'] = tables[i];
      return info;
    }
  }
  return null;
}

// TODO: Correct handling of non-breaking spaces (convert to spaces)
// TODO: It would be nice to have better itemText than just the options,
//  particularly for products that don't have any options.
function cartItemFromRow(row, tableInfo) {
  try {
    var itemName = innerText(row.cells[tableInfo['item']].getElementsByTagName("A")[1]);
    var itemText = "";
    if (typeof(tableInfo['options']) != "undefined") {
      itemText = innerText(row.cells[tableInfo['options']]);
    } 
    var itemPrice = innerText(row.cells[tableInfo['price']]).match(/\d+(\.\d+)?/)[0];
    var itemQuantity = row.cells[tableInfo['quantity']].getElementsByTagName("INPUT")[0].value;
    return {
      name: itemName,
      text: itemText,
      price: itemPrice,
      quantity: itemQuantity
    };
  } catch(e) {
    return null;
  }
}

function collectCartItems() {
  var tableInfo = findItemsTable();
  if (!tableInfo) {
    return null;
  }
  var baseTable = tableInfo['table'];
  var items = new Array();
  
  for (var i = tableInfo['headerRow'] + 1; i < baseTable.rows.length; ++i) {
    var cartItem = cartItemFromRow(baseTable.rows[i], tableInfo);
    if (cartItem) {
      items.push(cartItem);
    } else {
      break;
    }
  }
  return items;
}

function newInput(baseName, index, value) {
  var input = document.createElement("INPUT");
  input.setAttribute("type", "hidden");
  input.setAttribute("name", baseName + "_" + index);
  input.setAttribute("value", value);
  return input;
}

function submitItems(items) {
  var cartProcessorUrl =  "http://your.domain.com/processCart.php"; // EDIT_ME: cart processing URL
  var itemsForm = document.createElement("FORM");
  itemsForm.setAttribute("method", "POST");
  itemsForm.setAttribute("action", cartProcessorUrl);
  for (var i = 0; i < items.length; ++i) {
    itemsForm.appendChild(newInput("name", i, items[i].name));
    itemsForm.appendChild(newInput("text", i, items[i].text));
    itemsForm.appendChild(newInput("price", i, items[i].price));
    itemsForm.appendChild(newInput("quantity", i, items[i].quantity));
  }
  document.body.appendChild(itemsForm);
  itemsForm.submit();
}

function doGoogleCheckout() {
  var items = collectCartItems();
  if (!items || items.length == 0) {
    alert("You must have something in your cart to check out (or I couldn't find your cart).");
    return;
  }
  submitItems(items);
}

function safeDoGoogleCheckout() {
  try {
    doGoogleCheckout();
  } catch (e) {
    alert(e);
  }
  return false;
}

function getButtonActionClosure() {
  return function() {
           safeDoGoogleCheckout();
           return false;
         };
}

function buttonImageUrl(merchant) {
  return "http://checkout.google.com/buttons/checkout.gif?merchant_id="
           + merchant
           + "&w=168&h=44&style=white&variant=text&loc=en_US";
}

function appendButtonToDomNode(merchant, node) {
  var gcButton = document.createElement("input");
  gcButton.setAttribute("type", "image");
  gcButton.setAttribute("value", "Google Checkout");
  gcButton.setAttribute("alt", "Google Checkout");
  gcButton.setAttribute("src", buttonImageUrl(merchant));
  gcButton.onclick = getButtonActionClosure();
  var gcForm = document.createElement("form");
  gcForm.appendChild(gcButton);
  var gcDiv = document.createElement("div");
  gcDiv.appendChild(gcForm);
  node.appendChild(gcDiv);
}

function writeButtonHtml(merchant) {
  document.write("<div><form>");
  document.write("<input type=image onclick='return safeDoGoogleCheckout();'"
                      + "alt='Google Checkout' value='Google Checkout' "
                      + "src='" + buttonImageUrl(merchant) + "'>");
  document.write("</form></div>");
}

function addCheckoutButton(merchant, mode) {
  if (mode == "append") {
    appendButtonToDomNode(merchant, document.body);
  } else if (mode == "inplace") {
    writeButtonHtml(merchant);
  }
}

// Set the variable useGcMode to one of "append" or "inplace" before
// loading this script to get different button placement behavior:
//   append (default) - Drops the button at the bottom of the document's DOM.
//   inplace - Places the button at the current location. Only useful if the
//             document is still loading, so should only be used with in-doc
//             script tag loading.
var gcMode = "append";
if (typeof(useGcMode) != "undefined") {
  gcMode = useGcMode;
}

var merchantId = "0123456789";  // EDIT_ME: Google Checkout merchant ID
addCheckoutButton(merchantId, gcMode);
